/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package model;

import helper.Utilities;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

/**
 *
 * @author kuroshaki
 */
public class Torrent{
    //variabel
    private int length ;        //ukuran setiap piece dalam byte
    private boolean privat;     //optional, tidak perlu dituliskan dalam file metainfo
    private String fileName;   //nama file
    private long fileLength;    //ukuran file dalam byte
    private String md5sum;      //optional, tidak perlu dituliskan dalam file metainfo

    private String resultPath;

    private ArrayList<String> pieces;

    private String announce;    //URl dari tracker
    private String announceList;    //optional, tidak perlu dituliskan dalam file metainfo
    private String creationDate;    //optional, tidak perlu dituliskan dalam file metainfo
    private String comment;         //optional, tidak perlu dituliskan dalam file metainfo
    private String createdby;       //optional, tidak perlu dituliskan dalam file metainfo
    private String encoding;        //optional, tidak perlu dituliskan dalam file metainfo

    
    //methods
    //konstruktor
    public Torrent(){
        this.length     = PIECE_LENGTH;
        this.fileName   = "";
        this.fileLength = 0;
        
        this.announce   = "";

        this.pieces     = new ArrayList<String>();
    }

    public Torrent(String torrentPath){
        try {
            BufferedReader in = new BufferedReader(new FileReader(torrentPath));

            String[] str = in.readLine().split(" ");

            this.fileName = str[0];
            this.fileLength = Integer.parseInt(str[1]);
            this.length = Integer.parseInt(str[2]);
            this.announce = in.readLine();
            
            str = in.readLine().split(" ");

            this.pieces = new ArrayList<String>();
            for (int i = 0; i < str.length; ++i){
                this.pieces.add(str[i]);
            }

            this.resultPath = torrentPath.replace(".torrent", "");

            in.close();
        } catch (Exception ex) {
            
        }
    }

    public void createTorrent(String filePath){
        File file       = new File(filePath);

        this.length     = PIECE_LENGTH;
        this.fileName   = file.getName();
        this.fileLength = file.length();
        this.resultPath   = filePath;

        this.announce   = "";

        this.pieces     = new ArrayList<String>();

        byte[] bytes    = Utilities.getByteArrayFromFile(file);

        for (int i = 0; i < bytes.length; i += this.length){
            byte[] toSHA = null;
            int iterateTo = 0;
            if (i + this.length > bytes.length) {
                toSHA = new byte[bytes.length - i];
                iterateTo = bytes.length;
            } else {
                toSHA = new byte[this.length];
                iterateTo = i + this.length;
            }
            
            for (int j = i; j < iterateTo; ++j){
                toSHA[j - i] = bytes[j];
            }

            try {
                String push = Utilities.convertToSHA1(toSHA);
                this.pieces.add(push);
            } catch (Exception ex) {

            }
        }
    }

    public void saveToFile(String folderPath) {
        try {
            //File file = new File(folderPath + "/" +this.fileName);
            //file.createNewFile();
            BufferedWriter out = new BufferedWriter(new FileWriter(folderPath + "/" + this.fileName + ".torrent"));

            out.write(this.fileName + " ");
            out.write(this.fileLength + " ");
            out.write(this.length + "");
            out.newLine();
            out.write(this.announce);
            out.newLine();
            out.write(this.pieces.get(0));

            for (int i = 1; i < this.pieces.size(); ++i){
                out.write(" " + this.pieces.get(i));
            }
            out.newLine();

            out.close();
        } catch (IOException ex) {
            System.out.println("Make Torrent : File not found");
        }
    }

    //methods set
    public void setLength(int length){
        this.length = length;
    }

    public void setPieces(ArrayList<String> piece){
        this.pieces = piece;
    }

    public void setPrivat(boolean opsi){
        this.privat = opsi;
    }

    public void setFileName(String namafile){
        this.fileName = namafile;
    }

    public void setFileLength(int panjangfile){
        this.fileLength = panjangfile;
    }

    public void setMd5sum(String mdsum){
        this.md5sum = mdsum;
    }

    //methods get
    public long getLength(){
        return this.length;
    }

    public ArrayList<String> getPieces(){
        return this.pieces;
    }

    public byte[] getCompletedPiece(int piece) {
        boolean[] completed = compareDownloadedFile(resultPath);
        
        if (piece < completed.length && completed[piece]) {
            byte[] bytes    = Utilities.getByteArrayFromFile(new File(resultPath));
            StringBuilder sb = new StringBuilder();

            for (int i = length * piece, j = 0; i < bytes.length && j < length ; ++i, ++j) {
                sb.append((char)bytes[i]);
            }

            String res = sb.toString();
            byte[] retval = new byte[res.length()];

            for (int i = 0; i < retval.length; ++i) {
                retval[i] = (byte)sb.charAt(i);
            }

            return retval;
        }

        return null;
    }

    public String getResultPath() {
        return resultPath;
    }

    public boolean getPrivat(){
        return this.privat;
    }

    public String getFileName(){
        return this.fileName;
    }

    public long getFileLength(){
        return this.fileLength;
    }

    public String getMd5sum(){
        return this.md5sum;
    }

    public boolean[] compareDownloadedFile(String path) {
        boolean[] retval    = new boolean[pieces.size()];
        File file           = new File(path);

        byte[] bytes    = Utilities.getByteArrayFromFile(file);

        for (int i = 0, pieceNumber = 0; i < bytes.length; i += this.length, ++pieceNumber){
            byte[] toSHA = null;
            int iterateTo = 0;
            if (i + this.length > bytes.length) {
                toSHA = new byte[bytes.length - i];
                iterateTo = bytes.length;
            } else {
                toSHA = new byte[this.length];
                iterateTo = i + this.length;
            }

            for (int j = i; j < iterateTo; ++j){
                toSHA[j - i] = bytes[j];
            }

            try {
                String piece = Utilities.convertToSHA1(toSHA);
                if (piece.equals(this.pieces.get(pieceNumber))) {
                    retval[pieceNumber] = true;
                } else {
                    retval[pieceNumber] = false;
                }
            } catch (Exception ex) {

            }
        }

        return retval;
    }

    public void savePieceToResult(int piece, byte[] binary) {
        try {
            //RandomAccessFile ra = new RandomAccessFile
            RandomAccessFile raf = new RandomAccessFile(new File(resultPath), "rw");
            raf.seek(piece * this.length);
            //BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(resultPath));
            raf.write(binary);

            raf.close();
            //bos.write(binary, piece * this.length, binary.length);
        } catch (Exception ex) {
            System.out.println(ex);
        }
    }

    public static void main(String[] args) throws Exception {
        Torrent tor = new Torrent();
        tor.createTorrent("res/Tugas_Besar_1_IF3056.pdf");
        tor.saveToFile("res/");

        tor = new Torrent("res/Tugas_Besar_1_IF3056.pdf.torrent");
        boolean[] bol = tor.compareDownloadedFile("res/Tugas_Besar_1_IF3056.pdf");

        for (int i = 0; i < bol.length; ++i) {
            System.out.println(bol[i]);
        }
    }

    public final static int PIECE_LENGTH = 32768;
}
